package cn.uin.config;

import cn.uin.domain.auth.adapter.repository.IAuthRepository;
import cn.uin.domain.auth.service.user.login.email.EmailLoginFilter;
import cn.uin.domain.auth.service.user.login.email.EmailLoginProvider;
import cn.uin.domain.auth.service.user.login.email.handle.CustomEmailAuthenticationFailureHandler;
import cn.uin.domain.auth.service.user.login.email.handle.CustomEmailAuthenticationSuccessHandler;
import cn.uin.domain.auth.service.user.login.handle.*;
import cn.uin.domain.auth.service.user.login.CustomUserDetailService;
import cn.uin.domain.auth.service.user.login.jwt.JWTLoginFilter;
import cn.uin.domain.auth.service.user.login.jwt.handle.CustomJWTAuthenticationFailureHandler;
import cn.uin.domain.auth.service.user.login.jwt.handle.CustomJWTAuthenticationSuccessHandler;
import cn.uin.domain.auth.service.user.login.phone.PhoneLoginFilter;
import cn.uin.domain.auth.service.user.login.phone.PhoneLoginProvider;
import cn.uin.domain.auth.service.user.login.phone.handle.CustomPhoneAuthenticationFailureHandler;
import cn.uin.domain.auth.service.user.login.phone.handle.CustomPhoneAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.Collections;

/**
 * @ClassName SecurityConfig
 * @Description:SpringSecurity配置文件
 * @Author: Administrator
 * @CreateDate: 2025/10/15 17:54
 * @Version: 1.0
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	/**放行的路径*/
	private final String[] PATH_RELEASE = {
			"/**/*.js", "*.jpg", "/**/*.css",
			"/api/auth/login_pwd", "/api/auth/login_phone", "/api/auth/login_email",
			"/api/auth/register","/api/auth/sendEmailCode",
			"/api/auth/sendPhoneCode"
	};

	private final CustomUserDetailService customUserDetailService;

	private final CustomJWTAuthenticationFailureHandler customJWTAuthenticationFailureHandler;

	private final CustomJWTAuthenticationSuccessHandler customJWTAuthenticationSuccessHandler;

	private final CustomMacLoginUrlAuthenticationEntryPoint customMacLoginUrlAuthenticationEntryPoint;

	private final CustomAccessDeniedHandler customAccessDeniedHandler;

	private final CustomLogoutSuccessHandler customLogoutSuccessHandler;

	private final CustomPermissionEvaluator customPermissionEvaluator;

	private final IAuthRepository iAuthRepository;

	private final CustomPhoneAuthenticationSuccessHandler customPhoneAuthenticationSuccessHandler;

	private final CustomPhoneAuthenticationFailureHandler customPhoneAuthenticationFailureHandler;

	private final CustomEmailAuthenticationSuccessHandler customEmailAuthenticationSuccessHandler;

	private final CustomEmailAuthenticationFailureHandler customEmailAuthenticationFailureHandler;

	@Autowired
	public SecurityConfig(CustomUserDetailService customUserDetailService, CustomJWTAuthenticationFailureHandler customJWTAuthenticationFailureHandler,
	                      CustomJWTAuthenticationSuccessHandler customJWTAuthenticationSuccessHandler, CustomMacLoginUrlAuthenticationEntryPoint customMacLoginUrlAuthenticationEntryPoint,
	                      CustomAccessDeniedHandler customAccessDeniedHandler, CustomLogoutSuccessHandler customLogoutSuccessHandler, CustomPermissionEvaluator customPermissionEvaluator,
	                      IAuthRepository iAuthRepository, CustomPhoneAuthenticationSuccessHandler customPhoneAuthenticationSuccessHandler, CustomPhoneAuthenticationFailureHandler customPhoneAuthenticationFailureHandler,
	                      CustomEmailAuthenticationSuccessHandler customEmailAuthenticationSuccessHandler, CustomEmailAuthenticationFailureHandler customEmailAuthenticationFailureHandler) {
		this.customUserDetailService = customUserDetailService;
		this.customJWTAuthenticationFailureHandler = customJWTAuthenticationFailureHandler;
		this.customJWTAuthenticationSuccessHandler = customJWTAuthenticationSuccessHandler;
		this.customMacLoginUrlAuthenticationEntryPoint = customMacLoginUrlAuthenticationEntryPoint;
		this.customAccessDeniedHandler = customAccessDeniedHandler;
		this.customLogoutSuccessHandler = customLogoutSuccessHandler;
		this.customPermissionEvaluator = customPermissionEvaluator;
		this.iAuthRepository = iAuthRepository;
		this.customPhoneAuthenticationSuccessHandler = customPhoneAuthenticationSuccessHandler;
		this.customPhoneAuthenticationFailureHandler = customPhoneAuthenticationFailureHandler;
		this.customEmailAuthenticationSuccessHandler = customEmailAuthenticationSuccessHandler;
		this.customEmailAuthenticationFailureHandler = customEmailAuthenticationFailureHandler;
	}

	/**
	 * 认证
	 * @param auth
	 * @throws Exception
	 */
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(customUserDetailService).passwordEncoder(passwordEncoder());
		auth.authenticationProvider(phoneLoginProvider());
		auth.authenticationProvider(emailLoginProvider());
	}

	/**
	 * 授权
	 * @param http
	 * @throws Exception
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		/** 使用自定义的CORS配置源.and().csrf().disable(); */
		http.cors(cors -> cors.configurationSource(corsConfigurationSource())) // 启用CORS
				.csrf(csrf -> csrf.disable())

		.authorizeRequests()
				/** antMatchers (这里的路径)   permitAll 这里是允许所有人 访问 */
				.antMatchers(PATH_RELEASE).permitAll()

				/** 映射任何请求 */
				.anyRequest()

				/** 指定任何经过身份验证的用户都允许使用URL。*/
				.authenticated()

				/** 指定支持基于表单的身份验证 */
				.and().formLogin().permitAll()

				/** 允许配置异常处理。可以自己传值进去 使用WebSecurityConfigurerAdapter时，将自动应用此WebSecurityConfigurerAdapter 。*/
				.and().exceptionHandling()

				/** 设置要使用的AuthenticationEntryPoint。   macLoginUrlAuthenticationEntryPoint   验证是否登录*/
				.authenticationEntryPoint(customMacLoginUrlAuthenticationEntryPoint)

				/** 指定要使用的AccessDeniedHandler   处理拒绝访问失败。*/
				.accessDeniedHandler(customAccessDeniedHandler)

				/** 提供注销支持。 使用WebSecurityConfigurerAdapter时，将自动应用此WebSecurityConfigurerAdapter 。
				 *  默认设置是访问URL “ / logout”将使HTTP会话无效，清理配置的所有rememberMe()身份验证，清除SecurityContextHolder ，
				 *  然后重定向到“ / login？success”，从而注销用户*/
				.and().logout().logoutSuccessHandler(customLogoutSuccessHandler)

				/** 处理身份验证表单提交。 授予权限 */
				.and()
				.addFilterBefore(jwtLoginFilter(), UsernamePasswordAuthenticationFilter.class)
				.authenticationProvider(emailLoginProvider()).addFilterBefore(emailLoginFilter(), UsernamePasswordAuthenticationFilter.class)
				.authenticationProvider(phoneLoginProvider()).addFilterBefore(phoneLoginFilter(), UsernamePasswordAuthenticationFilter.class)

				/** 处理HTTP请求的BASIC授权标头，然后将结果放入SecurityContextHolder 。 */
				.addFilter(customAuthorizationFilter())

				/**不需要session */
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}

	@Bean
	public JWTLoginFilter jwtLoginFilter() throws Exception {
		JWTLoginFilter jwtLoginFilter = new JWTLoginFilter();
		jwtLoginFilter.setAuthenticationManager(authenticationManager());
		jwtLoginFilter.setAuthenticationSuccessHandler(customJWTAuthenticationSuccessHandler);
		jwtLoginFilter.setAuthenticationFailureHandler(customJWTAuthenticationFailureHandler);
		return jwtLoginFilter;
	}

	@Bean
	public EmailLoginFilter emailLoginFilter() throws Exception {
		EmailLoginFilter emailLoginFilter = new EmailLoginFilter();
		emailLoginFilter.setAuthenticationManager(authenticationManager());
		emailLoginFilter.setiAuthRepository(iAuthRepository);
		emailLoginFilter.setAuthenticationSuccessHandler(customEmailAuthenticationSuccessHandler);
		emailLoginFilter.setAuthenticationFailureHandler(customEmailAuthenticationFailureHandler);
		return emailLoginFilter;
	}

	@Bean
	public PhoneLoginFilter phoneLoginFilter() throws Exception {
		PhoneLoginFilter phoneLoginFilter = new PhoneLoginFilter();
		phoneLoginFilter.setAuthenticationManager(authenticationManager());
		phoneLoginFilter.setiAuthRepository(iAuthRepository);
		phoneLoginFilter.setAuthenticationSuccessHandler(customPhoneAuthenticationSuccessHandler);
		phoneLoginFilter.setAuthenticationFailureHandler(customPhoneAuthenticationFailureHandler);
		return phoneLoginFilter;
	}

	@Bean
	public CustomAuthorizationFilter customAuthorizationFilter() throws Exception {
		CustomAuthorizationFilter customAuthorizationFilter = new CustomAuthorizationFilter(authenticationManager());
		return customAuthorizationFilter;
	}

	@Bean
	public EmailLoginProvider emailLoginProvider() {
		return new EmailLoginProvider(iAuthRepository);
	}

	@Bean
	public PhoneLoginProvider phoneLoginProvider() {
		return new PhoneLoginProvider(iAuthRepository);
	}

	@Bean
	public DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler(){
		DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
		handler.setPermissionEvaluator(customPermissionEvaluator);
		return handler;
	}

	@Bean
	public BCryptPasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Bean
	public CorsConfigurationSource corsConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.addAllowedOriginPattern("http://localhost:*"); // 允许的源
		configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); // 允许的方法
		configuration.setAllowedHeaders(Collections.singletonList("*")); // 允许的头部
		configuration.setAllowCredentials(true); // 是否发送Cookies和HTTP认证基本字段
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration); // 对所有路径应用CORS策略
		return source;
	}
}
